package org.ala.tiktools.rpc.client_server.service;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Consumer;

import org.ala.tiktools.logs.LoggerFactory;
import org.ala.tiktools.rpc.plugin.interceptor.IRpcInterceptorChain;
import org.ala.tiktools.rpc.plugin.interceptor.RpcInterceptor;
import org.ala.tiktools.rpc.spring.RpcBeanConfiguration;
import org.ala.tiktools.tools.BeanTools;
import org.ala.tiktools.tools.CachingKVTools;
import org.slf4j.Logger;
import org.springframework.context.ApplicationContext;

/**
 * rpc服务端执行器
 * <p>  一个方法对应一个MethodExecutor
 *
 * @author ala
 * @date 2025-04-07 14:12
 */
public class RpcMethodExecutor {
	static Logger log = LoggerFactory.getLogger("rpc", "server");
	
	
    /**
     *  执行线程池
     */
    protected ThreadPoolExecutor threadPool;
    /**
     *
     */
    protected Object bean;
    /**
     *
     */
    protected Method method;
    /**
     *
     */
    protected String url;
    /**
     * 
     */
    protected ApplicationContext context;
    /**
     * 拦截器链
     */
    protected CachingKVTools<Method, IRpcInterceptorChain> chainMapper = new CachingKVTools<Method, IRpcInterceptorChain>(IRpcInterceptorChain.NULL, m -> {
    	//	优先取自己的拦截器
    	RpcInterceptor anno = m.getAnnotation(RpcInterceptor.class);
    	IRpcInterceptorChain chain = null;
    	if (anno != null && anno.chain() != null) {
    		chain = BeanTools.getBean(context, anno.chain());
    	}
    	if (chain == null) {
    		chain = BeanTools.getBean(context, RpcBeanConfiguration.BEAN_INTERCEPTOR_RECEIVER_CHAIN, IRpcInterceptorChain.class);
    	}
    	return chain;
    });


    public static RpcMethodExecutor build() {return new RpcMethodExecutor();}


    /**
     *  执行方法
     */
    public void invoke(Object[] args, Map<String, Object> metadata, Consumer<RpcExecuteResult> consumer) {
        try {
            threadPool.execute(() -> {
                try {
                	//	执行前置拦截器
                	IRpcInterceptorChain chain = chainMapper.get(method);
                	if (chain != null) {
                		chain.doInterceptor(method, args, metadata);
                	}
                	
                    Object rtn = method.invoke(bean, args);
                    
                    //	执行后置拦截器
                    if (chain != null) {
                    	try {
                    		chain.afterInterceptor(method, args, rtn, metadata);
                    	} catch (Exception e) {
                    		log.error("[RpcMethodExecutor invoke] 后置拦截器执行失败，但不打断原执行逻辑", e);
                    	}
                	}
                    
                    consumer.accept(RpcExecuteResult.build().success(true).rtn(rtn).metadata(metadata));
                } catch (Throwable e) {
                    consumer.accept(RpcExecuteResult.build().success(false).e(e));
                }
            });
        } catch (Exception e) {
            consumer.accept(RpcExecuteResult.build().success(false).e(e));
        }
    }


    public ThreadPoolExecutor getThreadPool() {
        return threadPool;
    }
    public void setThreadPool(ThreadPoolExecutor threadPool) {
        this.threadPool = threadPool;
    }
    public RpcMethodExecutor threadPool(ThreadPoolExecutor threadPool) {
        setThreadPool(threadPool);
        return this;
    }
    public Object getBean() {
        return bean;
    }
    public void setBean(Object bean) {
        this.bean = bean;
    }
    public RpcMethodExecutor bean(Object bean) {
        setBean(bean);
        return this;
    }
    public Method getMethod() {
        return method;
    }
    public void setMethod(Method method) {
        this.method = method;
    }
    public RpcMethodExecutor method(Method method) {
        setMethod(method);
        return this;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public RpcMethodExecutor url(String url) {
        setUrl(url);
        return this;
    }
	public ApplicationContext getContext() {
		return context;
	}
	public void setContext(ApplicationContext context) {
		this.context = context;
	}
	public RpcMethodExecutor context(ApplicationContext context) {
		setContext(context);
		return this;
	}
}
